package cn.geminis.everydata.service.controller;

import cn.geminis.core.data.query.FilterGroup;
import cn.geminis.core.data.query.QueryParameters;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.web.bind.annotation.*;

import java.util.Objects;
import java.util.Optional;
import java.util.regex.Pattern;

/**
 * @author puddi
 */
@RestController
@RequestMapping("/data")
@RequiredArgsConstructor
public class DataController {

    private final MongoTemplate mongoTemplate;

    private void buildCriteria(Criteria criteria, FilterGroup filterGroup) {
        if (Objects.isNull(filterGroup)) {
            return;
        }
        if ("and".equals(filterGroup.getType())) {
            filterGroup.getFilters().forEach(filter -> {
                var cta = criteria.and(filter.getField());
                switch (filter.getCompareType()) {
                    case "=":
                        cta.is(filter.getValue());
                        break;
                    case "like":
                        var pattern = Pattern.compile("^.*" + filter.getValue() + ".*$", Pattern.CASE_INSENSITIVE);
                        cta.regex(pattern);
                        break;
                    case ">":
                        cta.gt(filter.getValue());
                        break;
                    case ">=":
                        cta.gte(filter.getValue());
                        break;
                    case "<":
                        cta.lt(filter.getValue());
                        break;
                    case "<=":
                        cta.lte(filter.getValue());
                        break;
                    case "in":
                        cta.in(filter.getValue());
                        break;
                    default:
                        throw new RuntimeException("不支持的操作符:" + filter.getCompareType());
                }
            });
        } else {
//            criteria.orOperator()
        }
        filterGroup.getChildGroups().forEach(group -> buildCriteria(criteria, group));
    }

    private Criteria buildCriteria(QueryParameters queryParameters) {
        var criteria = new Criteria();
        buildCriteria(criteria, queryParameters.getFilterGroup());
        return criteria;
    }

    @PostMapping("/{collection}")
    public Page<JSONObject> findPage(@PathVariable String collection, @RequestBody final QueryParameters queryParameters) {
        var criteria = buildCriteria(queryParameters);
        var query = Query.query(criteria);
        var count = this.mongoTemplate.count(query, collection);
        query.skip((long) queryParameters.getPageIndex() * queryParameters.getPageSize())
                .limit(queryParameters.getPageSize());
        queryParameters.getSorts().forEach(sort -> query.with(Sort.by("asc".equals(sort.getOrder()) ? Sort.Direction.ASC : Sort.Direction.DESC, sort.getField())));

        var list = this.mongoTemplate.find(query, JSONObject.class, collection);

        return new PageImpl<>(list, Pageable.unpaged(), count);
    }

    @PutMapping("/{collection}/{id}")
    public void save(@PathVariable String collection, @PathVariable String id, @RequestBody JSONObject data) {
        data.put("_id", id);
        var old = this.get(collection, id);
        old.ifPresent(obj -> obj.putAll(data.getInnerMap()));
        var newObj = old.orElse(data);
        this.mongoTemplate.save(newObj, collection);
    }

    @DeleteMapping("/{collection}/{id}")
    public void delete(@PathVariable String collection, @PathVariable String id) {
        var query = Query.query(Criteria.where("_id").is(id));
        this.mongoTemplate.remove(query, collection);
    }

    @GetMapping("/{collection}/{id}")
    @ResponseBody
    public Optional<JSONObject> get(@PathVariable String collection, @PathVariable String id) {
        var query = Query.query(Criteria.where("_id").is(id));
        return Optional.ofNullable(this.mongoTemplate.findOne(query, JSONObject.class, collection));
    }

}
